﻿using System;
using System.Linq;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NumSharp.Backends;
using NumSharp.Backends.Unmanaged;

namespace NumSharp.UnitTest.Backends.Unmanaged.Math
{
    [TestClass]
    public class np_exp_tests
    {
        [TestMethod]
        public void Exp_0()
        {
            var arr = np.zeros(new Shape(5, 5));
            var ret = np.exp(arr);

            for (int i = 0; i < ret.size; i++)
            {
                Console.WriteLine(ret.GetAtIndex(i));
            }
            ret.GetData<double>().All(d => System.Math.Abs(d - 1.0) < 0.0001).Should().BeTrue();
        }

        [TestMethod]
        public void Exp_1()
        {
            var arr = np.ones(new Shape(5, 5));
            var ret = np.exp(arr);

            for (int i = 0; i < ret.size; i++)
            {
                Console.WriteLine(ret.GetAtIndex(i));
            }
            ret.GetData<double>().All(d => System.Math.Abs(d - System.Math.E) < 0.0001).Should().BeTrue();
        }

        /*
            # Values generated by NumSharp.UnitTest\Utilities\ndarray-generator.py
            a = np.arange(1, 81)
            a = a.reshape(5, 4, 4)
            b = a[:, :, 0]
            cSharp.asCode2D("b54", b)
            ret = np.exp(b)
            cSharp.asCode2D("ret54", ret)
        */
        static NDArray ret54 = new NDArray(new double[] {
            2.718281828459045, 148.4131591025766, 8103.083927575384, 442413.3920089205,
            24154952.7535753, 1318815734.4832146, 72004899337.38588, 3931334297144.042,
            214643579785916.06, 1.1719142372802612e+16, 6.398434935300549e+17, 3.4934271057485095e+19,
            1.9073465724950998e+21, 1.0413759433029089e+23, 5.685719999335932e+24, 3.10429793570192e+26,
            1.6948892444103338e+28, 9.253781725587787e+29, 5.052393630276104e+31, 2.7585134545231703e+33
            }, new Shape(new int[] { 5, 4 }));

        [TestMethod]
        public void Exp_Sliced()
        {
            var a = np.arange(1.0, 81.0);
            a = a.reshape(5, 4, 4);
            var b = a[":, :, 0"];
            var ret = np.exp(b);

            for (int i = 0; i < ret.size; i++)
            {
                Console.WriteLine(ret.GetAtIndex(i));
            }
            var actual = ret.GetData<double>();
            var expected = ret54.GetData<double>();
            for (int i = 0; i < actual.Count; i++)
            {
                (System.Math.Abs((actual[i] / expected[i]) - 1.0) < 0.0000001).Should().BeTrue();
            }
        }

        [TestMethod]
        public void ExpUpcast()
        {
            var right = np.zeros(new Shape(5, 5)).astype(NPTypeCode.Int32) + 5;
            var ret = np.exp(right, NPTypeCode.Double);
            ret.GetTypeCode.Should().Be(NPTypeCode.Double);
            ret.GetData<double>().All(d => System.Math.Abs(d - 148.4131591) < 0.0001).Should().BeTrue();

            for (int i = 0; i < ret.size; i++)
            {
                Console.WriteLine(ret.GetAtIndex(i));
            }
        }

        [TestMethod]
        public void ExpDowncast()
        {
            var right = np.zeros(new Shape(5, 5)).astype(NPTypeCode.Double) + 5;
            new Action(()=> np.exp(right, NPTypeCode.Byte)).Should().Throw<IncorrectTypeException>().Where(exception => exception.Message.Contains("No loop matching the specified signature and casting was found for"));
        }
    }
}
